using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

namespace Pinwheel.LSerialize
{
    public static unsafe partial class LSerializeExtension
    {
        internal static class Cache 
        {
            // [ThreadStatic]
            // private static Dictionary<int, List<int>> s_Lookup;
            [ThreadStatic]
            private static Dictionary<int, long> s_DictRefId;
            
            static Cache()
            {
                // s_Lookup = new Dictionary<int, List<int>>();
                s_DictRefId = new Dictionary<int, long>();
            }

            internal static bool CacheReference(ref int hash, ref long refId)
            {
                bool result = s_DictRefId.TryAdd(hash, refId);
                return result;
            }
            
            // internal static bool CacheReference<T>(T* buffer, int hash, long refId) where T: unmanaged, ILStreamBuffer
            // {
            //     Profiler.BeginSample("LSerializeExtension.Cache.Create");
            //     var buffHash = buffer->GetHashCode();
            //     if (!s_Lookup.TryGetValue(buffHash, out var list))
            //     {
            //         list = new List<int>();
            //         s_Lookup.TryAdd(buffHash, list);
            //         
            //     }
            //     list.Add(hash);
            //     bool result = s_DictRefId.TryAdd(hash, refId);
            //     Profiler.EndSample();
            //     return result;
            // }
            
            // internal static List<int> GetReference<T>(T buffer) where T: unmanaged, ILStreamBuffer
            // {
            //     var buffHash = buffer.GetHashCode();
            //     if (s_Lookup.TryGetValue(buffHash, out var list))
            //         return list;
            //     return null;
            // }
            
            // internal static bool BindReference<T>(T buffer, List<int> lookup) where T: unmanaged, ILStreamBuffer
            // {
            //     if (null == lookup) return false;
            //     return s_Lookup.TryAdd(buffer.GetHashCode(), lookup);
            // }
            //
            // internal static bool UnbindReference<T>(T buffer) where T: unmanaged, ILStreamBuffer
            // {
            //     return s_Lookup.Remove(buffer.GetHashCode(), out _);
            // }

            // internal static void BindToNewReference<T>(T src, T dst) where T: unmanaged, ILStreamBuffer
            // {
            //     var srcHash = src.GetHashCode();
            //     var dstHash = dst.GetHashCode();
            //     if (s_Lookup.TryGetValue(srcHash, out var list))
            //     {
            //         s_Lookup.TryAdd(dstHash, list);
            //         s_Lookup.Remove(srcHash, out _);
            //     }
            //     else
            //     {
            //         RemoveAllReference(&src);
            //     }
            // }
            
            internal static bool TryGetReference(ref int hash, out long refId)
            {
                return s_DictRefId.TryGetValue(hash, out refId);
            }
        
            internal static bool RemoveReference(ref int hash)
            {
                return s_DictRefId.Remove(hash, out _);
            }

            internal static void RemoveAllReference()
            {
                Profiler.BeginSample("LSerializeExtension.Cache.Remove");
                s_DictRefId.Clear();
                Profiler.EndSample();
            }
        
            // internal static void RemoveAllReference<T>(T* buffer) where T: unmanaged, ILStreamBuffer
            // {
            //     Profiler.BeginSample("LSerializeExtension.Cache.Remove");
            //     var buffHash = buffer->GetHashCode();
            //     if (!s_Lookup.TryGetValue(buffHash, out var list)) return;
            //     for (int i = 0; i < list.Count; i++)
            //         RemoveReference(list[i]);
            //     s_Lookup.Remove(buffHash, out _);
            //     Profiler.EndSample();
            // }
        }
        
        internal static class CacheObj
        {
            // [ThreadStatic]
            // private static Dictionary<int, List<long>> s_Lookup;
            // [ThreadStatic]
            // private static Dictionary<long, object> s_DictObj;
            
            [ThreadStatic] private static object[] s_Lookup;
            [ThreadStatic] private static int s_Index;

            static CacheObj()
            {
                // s_Lookup = new Dictionary<int, List<long>>();
                // s_DictObj = new Dictionary<long, object>();
                s_Index = 0;
                s_Lookup = new object[16];
            }
            
            internal static void CacheSerializeObj<O>(long refId, ref O obj)
            {
                if (null == obj) return;
                Profiler.BeginSample("LSerializeExtension.CacheObj.Create");
                s_Index = (refId > s_Index) ? (int) refId : s_Index;
                if (s_Index >= s_Lookup.Length)
                    Array.Resize(ref s_Lookup, s_Index << 1);
                s_Lookup[refId] = obj;
                Profiler.EndSample();
            }
            
            internal static bool TryGetSerializeObj<O>(long refId, out O obj)
            {
                if (refId < 0 || refId > s_Index)
                {
                    obj = default(O);
                    return false;
                }
                obj = (O) s_Lookup[refId];
                return null != obj;
            }

            internal static void RemoveAllSerializeObj()
            {
                s_Index = 0;
                Array.Clear(s_Lookup, 0, s_Lookup.Length);
            }
            
            // internal static void CacheSerializeObj<T, O>(T* buffer, long refId, ref O obj) where T: unmanaged, ILStreamBuffer
            // {
            //     if (null == obj) return;
            //     Profiler.BeginSample("LSerializeExtension.CacheObj.Create");
            //     var buffHash = buffer->GetHashCode();
            //     if (!s_Lookup.TryGetValue(buffHash, out var list))
            //     {
            //         list = new List<long>();
            //         s_Lookup.TryAdd(buffHash, list);
            //     }
            //     list.Add(refId);
            //     s_DictObj.TryAdd(refId, obj);
            //     Profiler.EndSample();
            // }
            
            // internal static bool TryGetSerializeObj<O>(long refId, out O obj)
            // {
            //     if (s_DictObj.TryGetValue(refId, out var o))
            //     {
            //         obj = (O) o;
            //         return null != obj;
            //     }
            //     obj = default(O);
            //     return false;
            // }
        
            // internal static bool RemoveSerializeObj(long refId)
            // {
            //     return s_DictObj.Remove(refId, out _);
            // }
        
            // internal static void RemoveAllSerializeObj<T>(T* buffer) where T: unmanaged, ILStreamBuffer
            // {
            //     Profiler.BeginSample("LSerializeExtension.CacheObj.Remove");
            //     var buffHash = buffer->GetHashCode();
            //     if (!s_Lookup.TryGetValue(buffHash, out var list)) return;
            //     for (int i = 0; i < list.Count; i++)
            //         RemoveSerializeObj(list[i]);
            //     s_Lookup.Remove(buffHash, out _);
            //     Profiler.EndSample();
            // }
        }
    }
}
